﻿using IoTClient.Common.Helpers;
using IoTClient.Enums;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Xml.Serialization;

namespace IoTClient
{
    /// <summary>
    /// 日记记录委托定义
    /// </summary>
    /// <param name="name"></param>
    /// <param name="ex"></param>
    /// <param name="alarmLevel"></param>
    public delegate void LoggerDelegate(string name, Exception ex = null, AlarmLevel alarmLevel = AlarmLevel.Info);

    /// <summary>
    /// Socket基类
    /// </summary>
    [Category("设备属性")]
    public abstract class SocketBase
    {
        /// <summary>
        ///
        /// </summary>
        [Category("设备属性")]
        public SocketBase()
        {
            Name = DateTime.Now.ToString();
        }

        /// <summary>
        /// 警告日志委托
        /// 为了可用性，会对异常网络进行重试。此类日志通过委托接口给出去。
        /// </summary>
        [XmlIgnore]
        [Browsable(false)]
        public LoggerDelegate Loger { get; set; }

        /// <summary>
        /// 报警将激发的事件
        /// </summary>
        [Browsable(false)]
        [XmlIgnore]
        public Action<Variable, AlarmEventArgs> Alarmer { get; set; }

        /// <summary>
        /// 是否采用Auto方式
        /// </summary>
        [Browsable(false)]
        [XmlIgnore]
        public bool IsAuto { get; set; } = true;

        ///// <summary>
        ///// 异常错误将激发的事件
        ///// </summary>
        //[XmlIgnore] public Action<object, Exception> Erroring { get; set; }
        /// <summary>
        /// 设备名
        /// </summary>
        [JsonProperty("设备名")]
        [DisplayName("设备名")]
        public string Name { get; set; } = "设备名";

        /// <summary>
        /// IP地址
        /// </summary>
        [JsonProperty("IP地址"), DisplayName("IP地址")] public string IpAddress { get; set; } = "192.168.0.120";

        /// <summary>
        /// IP地址
        /// </summary>
        [JsonProperty("端口"), DisplayName("端口号")] public int Port { get; set; } = 102;

        /// <summary>
        /// 连接ip地址和端口
        /// </summary>
        [Browsable(false)]
        [XmlIgnore]
        public IPEndPoint IpEndPoint { get; set; }

        /// <summary>
        /// 是否启用
        /// </summary>
        [JsonProperty("是否启用"), DisplayName("是否启用")] public bool IsActive { get; set; } = true;

        /// <summary>
        ///
        /// </summary>
        [JsonProperty("字节格式"), DisplayName("字节格式")] public EndianFormat Format { get; set; }

        /// <summary>
        /// 是否是第一次连接,用于判断重连是否等待
        /// </summary>
        [XmlIgnore, Browsable(false)] public bool IsFirstConnect { get; set; } = true;

        /// <summary>
        /// 统计重连次数
        /// </summary>
        [XmlIgnore, Browsable(false)] public int ReconnectionCount { get; set; } = 0;

        /// <summary>
        /// 读取一次全部变量花的时间
        /// </summary>
        [XmlIgnore, Browsable(false)] public double? CycleTime { get; set; } = 0;

        /// <summary>
        /// 超时时间
        /// </summary>
        [JsonProperty("超时时间毫秒"), DisplayName("超时时间")]
        public int Timeout { get; set; } = 1500;

        /// <summary>
        /// 通过判断socket的状态,确定当前是否是连接的
        /// </summary>
        [XmlIgnore, Browsable(false)] public bool Connected => socket?.Connected ?? false;

        /// <summary>
        /// 变量描述
        /// </summary>
        [JsonProperty("设备描述"), DisplayName("设备描述")] public string Description { get; set; } = $"描述这是一个什么样的的设备........";

        /// <summary>
        /// 所有的变量
        /// </summary>
        [XmlArray("变量集合")]
        [XmlArrayItem("变量"), DisplayName("所有的变量集合")]
        public List<Variable> AllVarableList { get; set; } = new List<Variable>() { new Variable() };

        /// <summary>
        /// 所有的变量地址,数据类型
        /// </summary>
        [XmlIgnore, Browsable(false)] public Dictionary<string, DataTypeEnum> VarableAddressList = new Dictionary<string, DataTypeEnum>();

        /// <summary>
        /// 所有变量的字典集合
        /// </summary>
        [XmlIgnore, Browsable(false)]
        public Dictionary<string, Variable> AllVarableDictionary { get; set; } = new Dictionary<string, Variable>();

        /// <summary>
        /// 分批缓冲区大小
        /// </summary>
        protected const int BufferSize = 4096;

        /// <summary>
        /// Socket实例
        /// </summary>
        protected Socket socket;

        /// <summary>
        /// 是否自动打开关闭
        /// </summary>
        protected bool isAutoOpen = true;

        /// <summary>
        /// 用于取消线程
        /// </summary>
        [XmlIgnore, Browsable(false)] public CancellationTokenSource Cts { get; set; } = new CancellationTokenSource();

        /// <summary>
        /// 连接（如果已经是连接状态会先关闭再打开）
        /// </summary>
        /// <returns></returns>
        protected abstract Result Connect();

        /// <summary>
        /// 打开连接（如果已经是连接状态会先关闭再打开）
        /// </summary>
        /// <returns></returns>
        public Result Open()
        {
            isAutoOpen = false;
            return Connect();
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        /// <returns></returns>
        protected Result Dispose()
        {
            Result result = new Result();
            try
            {
                socket?.SafeClose();
                return result;
            }
            catch (Exception ex)
            {
                result.IsSucceed = false;
                result.Err = ex.Message;
                result.Exception = ex;
                Loger?.Invoke($"socket Dispose失败:{ex.Message}", ex);
                return result;
            }
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        /// <returns></returns>
        public Result Close()
        {
            isAutoOpen = true;
            return Dispose();
        }

        /// <summary>
        /// Socket读取
        /// </summary>
        /// <param name="socket">socket</param>
        /// <param name="receiveCount">读取长度</param>
        /// <returns></returns>
        protected Result<byte[]> ReadSocket(Socket socket, int receiveCount)
        {
            var result = new Result<byte[]>();
            if (receiveCount < 0)
            {
                result.IsSucceed = false;
                result.Err = $"读取长度[receiveCount]为{receiveCount}";
                result.AddErr2List();
                return result;
            }

            byte[] receiveBytes = new byte[receiveCount];
            int receiveFinish = 0;
            while (receiveFinish < receiveCount)
            {
                // 分批读取
                int receiveLength = (receiveCount - receiveFinish) >= BufferSize ? BufferSize : (receiveCount - receiveFinish);
                try
                {
                    var readLeng = socket.Receive(receiveBytes, receiveFinish, receiveLength, SocketFlags.None);
                    if (readLeng == 0)
                    {
                        socket?.SafeClose();
                        result.IsSucceed = false;
                        result.Err = $"连接被断开";
                        result.AddErr2List();

                        return result;
                    }
                    receiveFinish += readLeng;
                }
                catch (SocketException ex)
                {
                    socket?.SafeClose();
                    if (ex.SocketErrorCode == SocketError.TimedOut)
                        result.Err = $"连接超时：{ex.Message}";
                    else
                        result.Err = $"连接被断开，{ex.Message}";
                    result.IsSucceed = false;
                    result.AddErr2List();
                    result.Exception = ex;
                    Loger?.Invoke($"Scoket读取失败,异常信息:{ex.Message}", ex);
                    return result;
                }
            }
            result.Value = receiveBytes;
            return result.EndTime();
        }

        /// <summary>
        /// 发送报文，并获取响应报文（建议使用SendPackageReliable，如果异常会自动重试一次）
        /// </summary>
        /// <param name="command">发送命令</param>
        /// <returns></returns>
        public abstract Result<byte[]> SendPackageSingle(byte[] command);

        /// <summary>
        /// 发送报文，并获取响应报文（如果网络异常，会自动进行一次重试）
        /// TODO 重试机制应改成用户主动设置
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public Result<byte[]> SendAndRevice(byte[] command)
        {
            try
            {
                var result = SendPackageSingle(command);
                if (!result.IsSucceed)
                {
                    Loger?.Invoke(result.Err, result.Exception);
                    //如果出现异常，则进行一次重试
                    var conentResult = Connect();
                    if (!conentResult.IsSucceed)
                        return new Result<byte[]>(conentResult);

                    return SendPackageSingle(command);
                }
                else
                    return result;
            }
            catch (Exception ex)
            {
                try
                {
                    Loger?.Invoke(ex.Message, ex);
                    //如果出现异常，则进行一次重试
                    var conentResult = Connect();
                    if (!conentResult.IsSucceed)
                        return new Result<byte[]>(conentResult);

                    return SendPackageSingle(command);
                }
                catch (Exception ex2)
                {
                    Result<byte[]> result = new Result<byte[]>();
                    result.IsSucceed = false;
                    result.Err = ex2.Message;
                    result.AddErr2List();
                    return result.EndTime();
                }
            }
        }

        #region Ext

        /// <summary>
        /// 检测报警变量
        /// </summary>
        /// <param name="var"></param>
        /// <returns></returns>
        public Result CheckAlarmVarable(Variable var)
        {
            Result result = new Result();
            switch (var.DataType)
            {
                case Enums.DataTypeEnum.Bool://为离散报警
                    if (var.DiscreteAlarmType)//上升沿报警
                    {
                        if (var.Value.ToString() == "1" && var.RiseCacheValue == false)//报警
                        {
                            Alarmer?.Invoke(var, new AlarmEventArgs()
                            {
                                IsTrigger = true,
                                AlarmInfo = var.DiscreteAlarmNote,
                                Value = var.Value.ToString(),
                                SetValue = 1
                            });
                            var.FallCacheValue = var.Value.ToString() == "1";
                        }
                        if (var.Value.ToString() == "0" && var.RiseCacheValue == true)//取消报警
                        {
                            Alarmer?.Invoke(var, new AlarmEventArgs()
                            {
                                IsTrigger = false,
                                AlarmInfo = var.DiscreteAlarmNote,
                                Value = var.Value.ToString(),
                                SetValue = 1
                            });
                            var.FallCacheValue = var.Value.ToString() == "1";
                        }
                    }
                    else//下降沿报警
                    {
                        if (var.Value.ToString() == "0" && var.FallCacheValue == true)//报警
                        {
                            Alarmer?.Invoke(var, new AlarmEventArgs()
                            {
                                IsTrigger = true,
                                AlarmInfo = var.DiscreteAlarmNote,
                                Value = var.Value.ToString(),
                                SetValue = 0
                            });
                            var.FallCacheValue = var.Value.ToString() == "1";
                        }
                        if (var.Value.ToString() == "1" && var.FallCacheValue == false)//取消报警
                        {
                            Alarmer?.Invoke(var, new AlarmEventArgs()
                            {
                                IsTrigger = false,
                                AlarmInfo = var.DiscreteAlarmNote,
                                Value = var.Value.ToString(),
                                SetValue = 0
                            });
                            var.FallCacheValue = var.Value.ToString() == "1";
                        }
                    }
                    //  var.FallCacheValue = var.Value.ToString() == "1";
                    break;

                case Enums.DataTypeEnum.Byte:
                case Enums.DataTypeEnum.Int16:
                case Enums.DataTypeEnum.UInt16:
                case Enums.DataTypeEnum.Int32:
                case Enums.DataTypeEnum.UInt32:
                case Enums.DataTypeEnum.Int64:
                case Enums.DataTypeEnum.UInt64:
                case Enums.DataTypeEnum.Float:
                case Enums.DataTypeEnum.Double://为条件报警
                    float value = Convert.ToSingle(var.Value);
                    if (var.HighAlarmEnable)
                    {
                        if (value > var.HighAlarmSetValue && var.HighCacheValue < var.HighAlarmSetValue)//触发报警
                        {
                            Alarmer?.Invoke(var, new AlarmEventArgs()
                            {
                                IsTrigger = true,
                                AlarmInfo = var.HighAlarmNote,
                                Value = var.Value.ToString(),
                                SetValue = var.HighAlarmSetValue
                            });
                            var.HighCacheValue = value;
                        }
                        if (value < var.HighAlarmSetValue && var.HighCacheValue > var.HighAlarmSetValue)
                        {
                            Alarmer?.Invoke(var, new AlarmEventArgs()
                            {
                                IsTrigger = false,
                                AlarmInfo = var.HighAlarmNote,
                                Value = var.Value.ToString(),
                                SetValue = var.HighAlarmSetValue
                            });
                            var.HighCacheValue = value;
                        }
                    }
                    if (var.HHighAlarmEnable)
                    {
                        if (value > var.HHighAlarmSetValue && var.HHighCacheValue < var.HHighAlarmSetValue)//触发报警
                        {
                            Alarmer?.Invoke(var, new AlarmEventArgs()
                            {
                                IsTrigger = true,
                                AlarmInfo = var.HHighAlarmNote,
                                Value = var.Value.ToString(),
                                SetValue = var.HHighAlarmSetValue
                            });
                            var.HHighCacheValue = value;
                        }
                        if (value < var.HHighAlarmSetValue && var.HHighCacheValue > var.HHighAlarmSetValue)
                        {
                            Alarmer?.Invoke(var, new AlarmEventArgs()
                            {
                                IsTrigger = false,
                                AlarmInfo = var.HHighAlarmNote,
                                Value = var.Value.ToString(),
                                SetValue = var.HHighAlarmSetValue
                            });
                            var.HHighCacheValue = value;
                        }
                    }
                    if (var.LowAlarmEnable)
                    {
                        if (value < var.LowAlarmSetValue && var.LowCacheValue > var.LowAlarmSetValue)//触发报警 低报警
                        {
                            Alarmer?.Invoke(var, new AlarmEventArgs()
                            {
                                IsTrigger = true,
                                AlarmInfo = var.LowAlarmNote,
                                Value = var.Value.ToString(),
                                SetValue = var.LowAlarmSetValue
                            });
                            var.LowCacheValue = value;
                        }
                        if (value > var.LowAlarmSetValue && var.LowCacheValue < var.LowAlarmSetValue)
                        {
                            Alarmer?.Invoke(var, new AlarmEventArgs()
                            {
                                IsTrigger = false,
                                AlarmInfo = var.LowAlarmNote,
                                Value = var.Value.ToString(),
                                SetValue = var.LowAlarmSetValue
                            });
                            var.LowCacheValue = value;
                        }
                    }
                    if (var.LLowAlarmEnable)
                    {
                        if (value < var.LLowAlarmSetValue && var.LLowCacheValue > var.LLowAlarmSetValue)//触发报警 低报警
                        {
                            Alarmer?.Invoke(var, new AlarmEventArgs()
                            {
                                IsTrigger = true,
                                AlarmInfo = var.LLowAlarmNote,
                                Value = var.Value.ToString(),
                                SetValue = var.LLowAlarmSetValue
                            });
                            var.LLowCacheValue = value;
                        }
                        if (value > var.LLowAlarmSetValue && var.LLowCacheValue < var.LLowAlarmSetValue)
                        {
                            Alarmer?.Invoke(var, new AlarmEventArgs()
                            {
                                IsTrigger = false,
                                AlarmInfo = var.LLowAlarmNote,
                                Value = var.Value.ToString(),
                                SetValue = var.LLowAlarmSetValue
                            });
                            var.LLowCacheValue = value;
                        }
                        //  var.LLowCacheValue = value;
                    }
                    break;

                default:
                    result.IsSucceed = false;
                    result.Err = "变量数据格式不正确";
                    break;
            }
            return result;
        }

        /// <summary>
        /// 开始设备:返回值信息里面填写是否启动成功
        /// </summary>
        /// <returns></returns>
        public abstract Result Start();

        /// <summary>
        /// 停止读取线程
        /// </summary>
        /// <returns></returns>
        public Result Stop()
        {
            Result result = new Result();
            try
            {
                if (Cts != null)
                {
                    Cts.Cancel();
                    Loger?.Invoke($"停止读取{Name}设备的线程成功");
                    result.SetErrInfo(Close());
                }
            }
            catch (Exception ex)
            {
                result.IsSucceed = false;
                result.Err = $"停止读取{Name}设备的线程失败";
                result.Exception = ex;
                Loger?.Invoke($"停止读取{Name}的线程失败:{ex.Message}");
                return result;
            }
            return result;
        }

        /// <summary>
        /// 自动写入
        /// </summary>
        /// <param name="dic">字典集合,变量地址为key,object的数据类型很重要 byte的1和int的1,是不一样的</param>
        /// <returns></returns>
        public virtual Result WriteAuto(Dictionary<string, object> dic)
        {
            return new Result();
        }

        /// <summary>
        /// 根据变量的地址直接获取值
        /// </summary>
        /// <param name="Address"></param>
        /// <returns></returns>
        public object this[string Address]
        {
            //todo 封装写入数据的方法,
            get
            {
                var add = this.Name + "_" + Address;
                if (AllVarableDictionary.ContainsKey(add) && AllVarableDictionary[add].Value != null)
                {
                    return AllVarableDictionary[add].Value;
                }
                else
                {
                    Loger?.Invoke("地址不对(查看配置表)?获取到的值为null?", null, AlarmLevel.Error);

                    return "地址错误?获取到的值为null?";
                }
            }
            set
            {
                var add = this.Name + "_" + Address;
                if (AllVarableDictionary.ContainsKey(add) && AllVarableDictionary[add].WriteOrRead != WriteOrRead.OnlyRead)
                {
                    Dictionary<string, object> dic = new Dictionary<string, object>() { { Address, value } };

                    var res = WriteAuto(dic);
                    if (!res.IsSucceed)
                    {
                        Loger?.Invoke($"写入失败:{res.Err}", res.Exception, AlarmLevel.Error);
                    }
                }
                else
                {
                    Loger?.Invoke("地址不对或者这个变量为不可以写入的变量(查看配置表)", null, AlarmLevel.Error);
                }
            }
        }

        #endregion Ext
    }
}